/*
 * @Author: czy
 * @Date: 2021-07-09 08:44:56
 * @LastEditTime: 2021-07-12 21:30:04
 */
#include "stdbool.h"
#include "string.h"
#include "stdarg.h"

#include "sys/container.h"
#include "sys/system.h"

int deque_full(void *deque)
{
    Deque *d = (Deque *)deque;
    return d->length == d->capacity;
}

int deque_empty(void *deque)
{
    Deque *d = (Deque *)deque;
    return d->length == 0;
}

int deque_lpush(void *deque, ...)
{
    if (deque_full(deque))
    {
        return false;
    }
    Deque *d = (Deque *)deque;
    if (d->data == NULL)
    {
        d->data = (char *)kmalloc(d->size * d->capacity);
    }
    va_list args;
    va_start(args, deque);
    if (!deque_empty(deque))
    {
        d->head = MOD((d->head - 1), d->capacity);
    }
    else
    {
        d->tail = MOD((d->tail + 1), d->capacity);
    }
    memcpy(&(d->data[d->head * d->size]), va_arg_by_size(args, d->size), d->size);
    va_end(args);
    d->length++;
    d->current = d->head;
    return true;
}

int deque_flpush(void *deque, ...)
{
    Deque *d = (Deque *)deque;
    if (d->data == NULL)
    {
        d->data = (char *)kmalloc(d->size * d->capacity);
    }
    if (deque_full(deque))
    {
        d->tail = MOD((d->tail - 1), d->capacity);
        d->length--;
    }
    va_list args;
    va_start(args, deque);
    if (!deque_empty(deque))
    {
        d->head = MOD((d->head - 1), d->capacity);
    }
    else
    {
        d->tail = MOD((d->tail + 1), d->capacity);
    }
    memcpy(&(d->data[d->head * d->size]), va_arg_by_size(args, d->size), d->size);
    va_end(args);
    d->length++;
    d->current = d->head;
    return true;
}

int deque_rpush(void *deque, ...)
{
    if (deque_full(deque))
    {
        return false;
    }
    Deque *d = (Deque *)deque;
    if (d->data == NULL)
    {
        d->data = (char *)kmalloc(d->size * d->capacity);
    }
    va_list args;
    va_start(args, deque);
    memcpy(&(d->data[d->tail * d->size]), va_arg_by_size(args, d->size), d->size);
    d->current = d->tail;
    d->tail = MOD((d->tail + 1), d->capacity);
    va_end(args);
    d->length++;
    return true;
}

int deque_frpush(void *deque, ...)
{
    Deque *d = (Deque *)deque;
    if (d->data == NULL)
    {
        d->data = (char *)kmalloc(d->size * d->capacity);
    }
    if (deque_full(deque))
    {
        d->head = MOD((d->head + 1), d->capacity);
        d->length--;
    }
    va_list args;
    va_start(args, deque);
    memcpy(&(d->data[d->tail * d->size]), va_arg_by_size(args, d->size), d->size);
    d->current = d->tail;
    d->tail = MOD((d->tail + 1), d->capacity);
    va_end(args);
    d->length++;
    return true;
}

void *deque_lpop(void *deque, void *data)
{
    if (deque_empty(deque))
    {
        return NULL;
    }
    Deque *d = (Deque *)deque;
    memcpy(data, &(d->data[d->head * d->size]), d->size);
    d->head = MOD((d->head + 1), d->capacity);
    d->length--;
    d->current = d->head;
    return data;
}

void *deque_rpop(void *deque, void *data)
{
    if (deque_empty(deque))
    {
        return NULL;
    }
    Deque *d = (Deque *)deque;
    d->tail = MOD((d->tail - 1), d->capacity);
    memcpy(data, &(d->data[d->tail * d->size]), d->size);
    d->length--;
    d->current = MOD((d->tail - 1), d->capacity);
    return data;
}

void *deque_get(void *deque, int index)
{
    Deque *d = (Deque *)deque;
    if (deque_empty(deque) && (index < -d->length || index >= d->length))
    {
        return NULL;
    }
    if (index >= 0)
    {
        d->current = MOD((d->head + index), d->capacity);
        return &(d->data[d->current * d->size]);
    }
    else
    {
        d->current = MOD((d->tail + index), d->capacity);
        return &(d->data[d->current * d->size]);
    }
}

void deque_clear(void *deque)
{
    Deque *d = (Deque *)deque;
    d->length = 0;
    d->head = 0;
    d->tail = 0;
    d->current = 0;
    kfree(d->data);
    d->data = NULL;
}

void *deque_next(void *deque)
{
    Deque *d = (Deque *)deque;
    if (d->current == d->capacity)
    {
        return NULL;
    }
    else if (d->current == -1)
    {
        d->current = d->head;
    }

    void *data = &(d->data[d->current * d->size]);
    if (d->current != MOD((d->tail - 1), d->capacity))
    {
        d->current = MOD((d->current + 1), d->capacity);
    }
    else
    {
        d->current = d->capacity;
    }
    
    return data;
}

void *deque_previous(void *deque)
{
    Deque *d = (Deque *)deque;
    if (d->current == -1)
    {
        return NULL;
    }
    else if (d->current == d->capacity)
    {
        d->current = MOD((d->tail - 1), d->capacity);
    }
    
    void *data = &(d->data[d->current * d->size]);
    if (d->current != d->head)
    {
        d->current = MOD((d->current - 1), d->capacity);
    }
    else
    {
        d->current = -1;
    }

    return data;
}